﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Diagnostics;
using System.Collections;

namespace KgUtils
{
    /// <summary>
    /// Helper class to provide parameter checks.
    /// 
    /// Inspired by the work of Alexander Jung, SDX AG
    /// See http://ajdotnet.wordpress.com/2009/08/01/posting-guards-guard-classes-explained/
    /// 
    /// </summary>
    public static class Guard
    {

        const string NULL_PARAM = "(no param)";
        const string NULL_VALUE = "(null)";

        /// <summary>
        /// Checks if a given value is not null and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        [DebuggerStepThrough]
        public static void AssertNotNull(object arg, string paramName)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName,
                "Argument ‘" + paramName ?? NULL_PARAM + "’ should not be NULL!");
        }

        /// <summary>
        /// Checks if a given value is not null and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="message">A message that describes the error. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        [DebuggerStepThrough]
        public static void AssertNotNull(object arg, string paramName, string message)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName, message);
        }

        /// <summary>
        /// Checks if a given value is not null and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="format">A composite format string.</param>
        /// <param name="args">An Object array containing zero or more objects to format. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        [DebuggerStepThrough]
        public static void AssertNotNull(object arg, string paramName, string format, params object[] args)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName, StringHelper.SafeFormat(format, args));
        }

        /// <summary>
        /// Checks if a given value is not null and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(string arg, string paramName)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName,
                "Argument ‘" + (paramName ?? NULL_PARAM) + "’ should not be NULL!");
            if (arg.Length == 0)
                throw new ArgumentOutOfRangeException(paramName, arg,
                "Argument ‘" + (paramName ?? NULL_PARAM) + "’ should not be empty!");
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="message">A message that describes the error. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(string arg, string paramName, string message)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName, message);
            if (arg.Length == 0)
                throw new ArgumentOutOfRangeException(paramName, arg, message);
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="format">A composite format string.</param>
        /// <param name="args">An Object array containing zero or more objects to format. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(string arg, string paramName, string format, params object[] args)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName, StringHelper.SafeFormat(format, args));
            if (arg.Length == 0)
                throw new ArgumentOutOfRangeException(paramName, arg, StringHelper.SafeFormat(format, args));
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(ICollection arg, string paramName)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName,
                "Argument ‘" + (paramName ?? NULL_PARAM) + "’ should not be NULL!");
            if (arg.Count == 0)
                throw new ArgumentOutOfRangeException(paramName, arg,
                "Argument ‘" + (paramName ?? NULL_PARAM) + "’ should not be empty!");
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="message">A message that describes the error. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(ICollection arg, string paramName, string message)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName, message);
            if (arg.Count == 0)
                throw new ArgumentOutOfRangeException(paramName, arg, message);
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="format">A composite format string.</param>
        /// <param name="args">An Object array containing zero or more objects to format. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(ICollection arg, string paramName, string format, params object[] args)
        {
            if (arg == null)
                throw new ArgumentNullException(paramName, StringHelper.SafeFormat(format, args));
            if (arg.Count == 0)
                throw new ArgumentOutOfRangeException(paramName, arg, StringHelper.SafeFormat(format, args));
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(Guid arg, string paramName)
        {
            if (arg == Guid.Empty)
                throw new ArgumentOutOfRangeException(paramName, arg,
                "Argument ‘" + (paramName ?? NULL_PARAM) + "’ should not be empty!");
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="message">A message that describes the error. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(Guid arg, string paramName, string message)
        {
            if (arg == Guid.Empty)
                throw new ArgumentOutOfRangeException(paramName, arg, message);
        }

        /// <summary>
        /// Checks if a given value is neither null nor empty and throws a respective exception otherwise.
        /// </summary>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="format">A composite format string.</param>
        /// <param name="args">An Object array containing zero or more objects to format. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertNotEmpty(Guid arg, string paramName, string format, params object[] args)
        {
            if (arg == Guid.Empty)
                throw new ArgumentOutOfRangeException(paramName, arg, StringHelper.SafeFormat(format, args));
        }

        /// <summary>
        /// Checks if a given condition is met and throws a respective exception otherwise.
        /// </summary>
        /// <param name="condition">The condition that has to be true, otherwise an exception is thrown.</param>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="format">A composite format string.</param>
        /// <param name="args">An Object array containing zero or more objects to format. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertCondition(bool condition, string paramName, object arg, string format, params object[] args)
        {
            if (!condition)
                throw new ArgumentOutOfRangeException(paramName, arg ?? NULL_VALUE, StringHelper.SafeFormat(format, args));
        }

        /// <summary>
        /// Checks if a given condition is met and throws a respective exception otherwise.
        /// </summary>
        /// <param name="condition">The condition that has to be true, otherwise an exception is thrown.</param>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <param name="message">A message that describes the error. </param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertCondition(bool condition, string paramName, object arg, string message)
        {
            if (!condition)
                throw new ArgumentOutOfRangeException(paramName, arg ?? NULL_VALUE, message);

        }

        /// <summary>
        /// Checks if a given condition is met and throws a respective exception otherwise.
        /// </summary>
        /// <param name="condition">The condition that has to be true, otherwise an exception is thrown.</param>
        /// <param name="arg">The value of the parameter that should be checked.</param>
        /// <param name="paramName">The name of the parameter that caused the exception.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is empty.</exception>
        [DebuggerStepThrough]
        public static void AssertCondition(bool condition, string paramName, object arg)
        {
            if (!condition)
                throw new ArgumentOutOfRangeException(paramName, arg ?? NULL_VALUE, "Condition not met!");
        }


        /// <summary>
        /// Check that an argument is in the specified range and throws ArgumentOutOfRangeException if not
        /// </summary>
        /// <typeparam name="T">Argument type</typeparam>
        /// <param name="value">Current value</param>
        /// <param name="paramName">Argument's name</param>
        /// <param name="min">Min value</param>
        /// <param name="max">Max value</param>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value is not in the specified range.</exception>
        /// [DebuggerStepThrough]
        public static void AssertRange<T>(T value, string paramName, T min, T max, bool allowEquals = true) where T : IComparable<T>
        {
            if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0 || (!allowEquals && value.CompareTo(min) == 0) || (!allowEquals && value.CompareTo(max) == 0))
                throw new ArgumentOutOfRangeException(paramName, value.ToString() ?? NULL_VALUE, "Argument not in the specified range");
        }
    }
}
